﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.ComponentModel;


namespace IVisual._SC.AutoDiscovery
{
    /// <summary>
    /// AD Server
    /// </summary>
    public class cls_AutoDiscovery_Server
    {
        private System.ComponentModel.BackgroundWorker workerUDP;

        // Port auf dem der Server reagiert
        public int AutoDiscoveryPort = 18500;

        // Erkennungsmerkmal des Dienstes
        public byte[] packetBytes = new byte[] { 0x1, 0x2, 0x3 };

        // AD-Antwort-IP
        public IPAddress _rec_IP;
        
        // AD-Antwort-Port
        public int _rec_Port = 0;

        private bool disposing = false;

        public cls_AutoDiscovery_Server(ref BackgroundWorker workerUDP, string serverIP, int serverPort)
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.INFO, "Starte AD-Server: " + serverIP + ":" + serverPort);
            this.workerUDP = workerUDP;
            this._rec_Port = serverPort;
            this._rec_IP = IPAddress.Parse(serverIP);

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
        }

        /// <summary>
        /// Stops the server
        /// </summary>
        public void Stop()
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            workerUDP.CancelAsync();
            workerUDP.Dispose();
            this.disposing = true;

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
        }

        /// <summary>
        /// Start the Server
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void Start(object sender, DoWorkEventArgs e)
        {
            try
            {
                this.workerUDP.ReportProgress(30, "AutoDiscovery::Service Listening " + this.AutoDiscoveryPort + "/UDP");
                byte[] ReceivedData = new byte[1024];


                // Localer End-Point
                IPEndPoint LocalEP = new IPEndPoint(IPAddress.Any, AutoDiscoveryPort);
                IPEndPoint RemoteEP = new IPEndPoint(IPAddress.Any, 0);

                UdpClient newsock = new UdpClient(LocalEP);

                ReceivedData = newsock.Receive(ref RemoteEP);

                IPEndPoint IncomingIP = (IPEndPoint)RemoteEP;

                while (!disposing)
                {
                    if (ReceivedData.SequenceEqual(packetBytes))
                    {
                        this.workerUDP.ReportProgress(1, "Anfrage von " + IncomingIP + "/UDP");
                        IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Anfrage von " + IncomingIP + "/UDP");

                        byte[] packetBytesAck = Encoding.ASCII.GetBytes("ACK " + _rec_IP + " " + _rec_Port); // Ack

                        newsock.Send(packetBytesAck, packetBytesAck.Length, RemoteEP);

                        this.workerUDP.ReportProgress(1, "Antworte(ACK) " + packetBytes.Length + " bytes zu " + IncomingIP);
                        IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Antworte(ACK) " + packetBytes.Length + " bytes zu " + IncomingIP);
                    }
                    else
                    {
                        // Unknown packet type.
                        this.workerUDP.ReportProgress(1, "Antworte(NAK) " + packetBytes.Length + " bytes zu " + IncomingIP);
                        IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Antworte(NAK) " + packetBytes.Length + " bytes zu " + IncomingIP);
                        byte[] packetBytesNak = Encoding.ASCII.GetBytes("NAK"); // NAK

                        newsock.Send(packetBytesNak, packetBytesNak.Length, RemoteEP);
                    }
                    ReceivedData = newsock.Receive(ref RemoteEP);
                }
            }
            catch (Exception ex)
            {
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.ERROR, ex.Message);
            }

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
        }
    }
}


